/*
 * Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <chrono>
#include "DetectionInfer.h"
#include "JsonSerialize.h"


DetectionInfer::DetectionInfer() {

    APP_ERROR ret = Init();
    if (ret != APP_ERR_OK) {
        LogError << "Input file is invalid";
    }    
}

DetectionInfer::~DetectionInfer() {

}

APP_ERROR DetectionInfer::Init() {

    APP_ERROR ret = CheckFileVaild(m_model_path);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    ret = CheckFileVaild(m_config_path);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    ret = CheckFileVaild(m_label_path);
    if (ret != APP_ERR_OK) {
        return ret;
    }

    m_pModel = std::make_shared<Model>(m_model_path, m_npuDeviceId);
    if (m_pModel == nullptr) {
        LogError << "Create model failed...";
	return APP_ERR_COMM_FAILURE;
    }

    m_pModelPostProcess = std::make_shared<Yolov7PostProcess>();
    if (m_pModelPostProcess == nullptr) {
        LogError << "Create postprocess instance failed...";
	return APP_ERR_COMM_FAILURE;
    }

    std::map<std::string, std::string> postConfig;
    postConfig.insert(pair<std::string, std::string>("postProcessConfigPath", m_config_path));
    postConfig.insert(pair<std::string, std::string>("labelPath", m_label_path));

    ret =  m_pModelPostProcess->Init(postConfig);
    if (ret != APP_ERR_OK) {
        return ret;
    }
    return APP_ERR_OK;
}


void DetectionInfer::drawBoundingBox(cv::Mat& image, const std::string& className, int x0, int y0, int x1, int y1)
{
    cv::Rect boundingBox(x0, y0, x1 - x0, y1 - y0);
    cv::rectangle(image, boundingBox, cv::Scalar(0, 255, 0), 2);
    cv::putText(image, className, cv::Point(x0, y0 - 10), cv::FONT_HERSHEY_SIMPLEX, 0.9, cv::Scalar(0, 255, 0), 2);
}


APP_ERROR DetectionInfer::CheckFileVaild(const std::string &filePath)
{
    struct stat buf;
    if (lstat(filePath.c_str(), &buf) != 0 || S_ISLNK(buf.st_mode)) {
        LogError << "Input file is invalid and cannot be a link";
        return APP_ERR_COMM_NO_EXIST;
    }
    char c[PATH_MAX + 1] = {0x00};
    size_t count = filePath.copy(c, PATH_MAX + 1);
    if (count != filePath.length()) {
        LogError << "Failed to copy file path.";
        return APP_ERR_COMM_FAILURE;
    }
    char path[PATH_MAX + 1] = {0x00};
    if (realpath(c, path) == nullptr) {
        LogError << "Failed to get the file.";
        return APP_ERR_COMM_NO_EXIST;
    }
    FILE *fp = fopen(path, "rb");
    if (fp == nullptr) {
        LogError << "Failed to open file";
        return APP_ERR_COMM_OPEN_FAIL;
    }
    fseek(fp, 0, SEEK_END);
    long fileSize = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    if (fileSize <= 0 || fileSize > MAX_FILE_SIZE) {
        fclose(fp);
        return APP_ERR_COMM_FAILURE;
    }
    fclose(fp);
    return APP_ERR_OK;
}

std::vector<std::vector<ObjectInfo>> DetectionInfer::SDKPostProcess(std::vector<Tensor> &yolov7Outputs, 
		    std::vector<ResizedImageInfo> &imagePreProcessInfos)
{
       
    std::vector<TensorBase> tensors;
    for (size_t i = 0; i < yolov7Outputs.size(); i++) {
        MemoryData memoryData(yolov7Outputs[i].GetData(), yolov7Outputs[i].GetByteSize());
        TensorBase tensorBase(memoryData, true, yolov7Outputs[i].GetShape(), TENSOR_DTYPE_INT32);
        tensors.push_back(tensorBase);
    }

    std::vector<std::vector<ObjectInfo>> objectInfos;

    m_pModelPostProcess->SetObjectThreshold(0.5);
    APP_ERROR ret = m_pModelPostProcess->Process(tensors, objectInfos, imagePreProcessInfos);
    if (ret != APP_ERR_OK) {
        LogError << "Model post process failed...";
    }

#if DRAW_IMAGE
    std::string imagePath = "./test_client/1.jpg";
    cv::Mat image = cv::imread(imagePath);
    if (image.empty()) {
        LogError << "get image data error ...";
        return objectInfos;
    }

    for (size_t i = 0; i < objectInfos.size(); i++) {

	//std::cout  << "objectInfos- item" << i;
        for (size_t j = 0; j < objectInfos[i].size(); j++) {
		std::cout << " objectInfo-" << j;
		std::cout << "      x0 is:" << objectInfos[i][j].x0;
		std::cout << "      y0 is:" << objectInfos[i][j].y0;
		std::cout << "      x1 is:" << objectInfos[i][j].x1;
		std::cout << "      y1 is:" << objectInfos[i][j].y1;
		std::cout << "      confidence is: " << objectInfos[i][j].confidence;
		std::cout << "      classId is: " << objectInfos[i][j].classId;
		std::cout << "      className is: " << objectInfos[i][j].className;
		std::cout << std::endl;

		drawBoundingBox(image, objectInfos[i][j].className,
			       	objectInfos[i][j].x0, objectInfos[i][j].y0, 
				objectInfos[i][j].x1, objectInfos[i][j].y1);
        }
    }
    cv::imwrite("22.jpg", image);
#endif

    return objectInfos;
}

APP_ERROR DetectionInfer::PaddingProcess(ImageProcessor &imageProcessor, std::pair<int, int> resizeInfo,
    Image &resizeImage, Image &pastedImg)
{
    int resizedWidth = resizeInfo.first;
    int resizedHeight = resizeInfo.second;
    int leftOffset = (MODEL_INPUT_WIDTH - resizedWidth) / AVG_PARAM;
    int topOffset = (MODEL_INPUT_HEIGHT - resizedHeight) / AVG_PARAM;
    uint32_t dataSize = MODEL_INPUT_WIDTH * MODEL_INPUT_HEIGHT * RGB_EXTEND;
    MxBase::Size imageSize(MODEL_INPUT_WIDTH, MODEL_INPUT_HEIGHT);
    if (leftOffset > 0) {
        MemoryData srcData(resizeImage.GetData().get(), resizeImage.GetDataSize(), MemoryData::MemoryType::MEMORY_DVPP, m_npuDeviceId);
        MemoryData resHostData(nullptr, resizeImage.GetDataSize(), MemoryData::MemoryType::MEMORY_HOST, -1);
        if (MemoryHelper::MxbsMallocAndCopy(resHostData, srcData) != APP_ERR_OK) {
            LogError << "Failed to mallloc and copy dvpp memory.";
            return APP_ERR_ACL_BAD_COPY;
        }
        cv::Mat resizedHost(resizeImage.GetSize().height, resizeImage.GetSize().width, OPENCV_8UC3,
            resHostData.ptrData);
        cv::Rect roi = cv::Rect(0, 0, resizedWidth, resizedHeight);
        cv::Mat extendedImage;
        cv::copyMakeBorder(resizedHost(roi), extendedImage, 0, 0, leftOffset,
            MODEL_INPUT_WIDTH - leftOffset - resizedWidth, cv::BORDER_CONSTANT,
            cv::Scalar(PAD_COLOR, PAD_COLOR, PAD_COLOR));
        int maxFillRow = std::min(MODEL_INPUT_WIDTH, (int)resizeImage.GetSize().width + leftOffset);
        for (int col = 0; col < MODEL_INPUT_WIDTH; col++) {
            for (int row = resizedWidth + leftOffset; row < maxFillRow; row++) {
                extendedImage.at<cv::Vec3b>(col, row)[0] = PAD_COLOR;
                extendedImage.at<cv::Vec3b>(col, row)[1] = PAD_COLOR;
                extendedImage.at<cv::Vec3b>(col, row)[R_CHANNEL] = PAD_COLOR;
            }
        }
        uint8_t* pasteHostData = (uint8_t*)malloc(dataSize);
        if (pasteHostData == nullptr) {
            return APP_ERR_ACL_BAD_ALLOC;
        }
        for (size_t i = 0; i < dataSize; i++) {
            pasteHostData[i] = extendedImage.data[i];
        }
        std::shared_ptr<uint8_t> dataPaste((uint8_t*)pasteHostData, free);
        Image pastedImgTmp(dataPaste, dataSize, -1, imageSize, ImageFormat::BGR_888);
        pastedImgTmp.ToDevice(0);
        pastedImg = pastedImgTmp;
    } else {
        MemoryData imgData(dataSize, MemoryData::MemoryType::MEMORY_DVPP, m_npuDeviceId);
        if (MemoryHelper::Malloc(imgData) != APP_ERR_OK) {
            return APP_ERR_ACL_BAD_ALLOC;
        }
        std::shared_ptr<uint8_t> pastedData((uint8_t*)imgData.ptrData, imgData.free);
        if (MemoryHelper::Memset(imgData, PAD_COLOR, dataSize) != APP_ERR_OK) {
            LogError << "Failed to memset dvpp memory.";
            return APP_ERR_ACL_BAD_ALLOC;
        }
        Rect RectSrc(0, 0, resizedWidth, resizedHeight);
        Rect RectDst(leftOffset, topOffset, leftOffset + resizedWidth, topOffset + resizedHeight);
        std::pair<Rect, Rect> cropPasteRect = {RectSrc, RectDst};
        Image pastedImgTmp(pastedData, dataSize, m_npuDeviceId, imageSize, ImageFormat::BGR_888);
        if (imageProcessor.CropAndPaste(resizeImage, cropPasteRect, pastedImgTmp) != APP_ERR_OK) {
            LogError << "Failed to padding the image by dvpp";
            return APP_ERR_COMM_FAILURE;
        }
        pastedImg = pastedImgTmp;
    }
    return APP_ERR_OK;
}

APP_ERROR DetectionInfer::SetImageBackground(MxBase::MemoryData& data)
{
    auto dataPtr = data.ptrData;
    float yuvY = YUV_Y_R * PAD_COLOR + YUV_Y_G * PAD_COLOR + YUV_Y_B * PAD_COLOR;
    float yuvU = YUV_U_R * PAD_COLOR - YUV_U_G * PAD_COLOR + YUV_U_B * PAD_COLOR + YUV_OFFSET_UV;
    float yuvV = YUV_V_R * PAD_COLOR - YUV_V_G * PAD_COLOR - YUV_V_B * PAD_COLOR + YUV_OFFSET_UV;

    APP_ERROR ret = MxBase::MemoryHelper::MxbsMemset(data, (int)yuvY, data.size);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to memset dvpp memory";
        return ret;
    }
    int offsetSize = MODEL_INPUT_HEIGHT * MODEL_INPUT_WIDTH / YUV_OFFSET;
    data.ptrData = (uint8_t *)data.ptrData + MODEL_INPUT_HEIGHT * MODEL_INPUT_WIDTH;
    ret = MxBase::MemoryHelper::MxbsMemset(data, (int)yuvU, offsetSize);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to memset dvpp memory";
        data.ptrData = dataPtr;
        return ret;
    }
    data.ptrData = (uint8_t *)data.ptrData + YUV_OFFSET_S;
    for (int i = 0; i < offsetSize / YUV_OFFSET; i++) {
        ret = MxBase::MemoryHelper::MxbsMemset(data, (int)yuvV, YUV_OFFSET_S);
        if (ret != APP_ERR_OK) {
            LogError << "Failed to memset dvpp memory";
            data.ptrData = dataPtr;
            return ret;
        }
        data.ptrData = (uint8_t *)data.ptrData + YUV_OFFSET;
    }
    data.ptrData = dataPtr;
    return APP_ERR_OK;
}

APP_ERROR DetectionInfer::DvppPreprocessorYuv(ImageProcessor &imageProcessor, std::string &imagePath, vector<Tensor> &yolov7Inputs,
    std::vector<ResizedImageInfo> &imagePreProcessInfos)
{
    Image decodeImage;
    APP_ERROR ret = imageProcessor.Decode(imagePath, decodeImage);
    if (ret != APP_ERR_OK) {
        LogError << "ImageProcessor decode failed.";
        return ret;
    }

    Image resizeImage;
    uint32_t originalWidth = decodeImage.GetOriginalSize().width;
    uint32_t originalHeight = decodeImage.GetOriginalSize().height;
    float scaleWidth = MODEL_INPUT_WIDTH * 1.0 / originalWidth;
    float scaleHeight = MODEL_INPUT_HEIGHT * 1.0 / originalHeight;
    float minScale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
    int resizedWidth = std::round(originalWidth * minScale);
    int resizedHeight = std::round(originalHeight * minScale);
    ret = imageProcessor.Resize(decodeImage, MxBase::Size(resizedWidth, resizedHeight), resizeImage,
        Interpolation::BILINEAR_SIMILAR_OPENCV);
    if (ret != APP_ERR_OK) {
        LogError << "ImageProcessor resize failed.";
        return ret;
    }
    uint32_t dataSize = MODEL_INPUT_WIDTH * MODEL_INPUT_HEIGHT * RGB_EXTEND / YUV_DIVISION;
    MxBase::Size imageSize(MODEL_INPUT_WIDTH, MODEL_INPUT_HEIGHT);
    MemoryData imgData(dataSize, MemoryData::MemoryType::MEMORY_DVPP, m_npuDeviceId);
    if (MemoryHelper::Malloc(imgData) != APP_ERR_OK) {
        LogError << "Failed to malloc dvpp memory.";
        return APP_ERR_ACL_BAD_ALLOC;
    }
    std::shared_ptr<uint8_t> pastedData((uint8_t*)imgData.ptrData, imgData.free);
    if (SetImageBackground(imgData) != APP_ERR_OK) {
        LogError << "Failed to memset dvpp memory.";
        return APP_ERR_ACL_BAD_ALLOC;
    }
    int leftOffset = (MODEL_INPUT_WIDTH - resizedWidth) / AVG_PARAM;
    int topOffset = (MODEL_INPUT_HEIGHT - resizedHeight) / AVG_PARAM;
    topOffset = topOffset % AVG_PARAM == 0 ? topOffset : topOffset - 1;
    leftOffset = leftOffset < ALIGN_LEFT ? 0 : leftOffset / ALIGN_LEFT * ALIGN_LEFT;
    Rect RectSrc(0, 0, resizedWidth, resizedHeight);
    Rect RectDst(leftOffset, topOffset, leftOffset + resizedWidth, topOffset + resizedHeight);
    std::pair<Rect, Rect> cropPasteRect = {RectSrc, RectDst};
    Image pastedImgTmp(pastedData, dataSize, m_npuDeviceId, imageSize, ImageFormat::YUV_SP_420);

    std::cout << "width:" << pastedImgTmp.GetOriginalSize().width << std::endl;
    std::cout << "Height:" << pastedImgTmp.GetOriginalSize().height << std::endl;
    if (imageProcessor.CropAndPaste(resizeImage, cropPasteRect, pastedImgTmp) != APP_ERR_OK) {
        LogError << "Failed to padding the image by dvpp";
        return APP_ERR_COMM_FAILURE;
    }
    yolov7Inputs.push_back(pastedImgTmp.ConvertToTensor());
    ResizedImageInfo imagePreProcessInfo(resizedWidth, resizedHeight, originalWidth, originalHeight,
        RESIZER_MS_KEEP_ASPECT_RATIO, minScale);
    imagePreProcessInfos.push_back(imagePreProcessInfo);

    return APP_ERR_OK;
}

APP_ERROR DetectionInfer::OpenCVPreProcessor(std::string &imagePath, vector<Tensor> &yolov7Inputs,
    std::vector<ResizedImageInfo> &imagePreProcessInfos)
{
    auto image = cv::imread(imagePath);
    size_t originalWidth = image.cols;
    size_t originalHeight = image.rows;
    float scaleWidth = MODEL_INPUT_WIDTH * 1.0 / originalWidth;
    float scaleHeight = MODEL_INPUT_HEIGHT * 1.0 / originalHeight;
    float minScale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
    int resizedWidth = std::round(originalWidth * minScale);
    int resizedHeight = std::round(originalHeight * minScale);

    cv::Mat resizedImg;
    cv::resize(image, resizedImg, cv::Size(resizedWidth, resizedHeight));
    int leftOffset = (MODEL_INPUT_WIDTH - resizedWidth) / AVG_PARAM;
    int topOffset = (MODEL_INPUT_HEIGHT - resizedHeight) / AVG_PARAM;
    uint32_t dataSize = MODEL_INPUT_HEIGHT * MODEL_INPUT_WIDTH * RGB_EXTEND;

    MxBase::Size imageSize(MODEL_INPUT_WIDTH, MODEL_INPUT_HEIGHT);
    cv::Mat extendedImage;
    cv::copyMakeBorder(resizedImg, extendedImage, topOffset, MODEL_INPUT_HEIGHT - topOffset - resizedHeight, leftOffset,
        MODEL_INPUT_WIDTH - leftOffset - resizedWidth, cv::BORDER_CONSTANT,
        cv::Scalar(PAD_COLOR, PAD_COLOR, PAD_COLOR));

    uint8_t *pasteHostData = (uint8_t *)malloc(dataSize);
    if (pasteHostData == nullptr) {
        return APP_ERR_ACL_BAD_ALLOC;
    }

    for (size_t i = 0; i < dataSize; i++) {
        pasteHostData[i] = extendedImage.data[i];
    }

    std::shared_ptr<uint8_t> dataPaste((uint8_t *)pasteHostData, free);
    Image pastedImage(dataPaste, dataSize, -1, imageSize, ImageFormat::BGR_888);
    pastedImage.ToDevice(m_npuDeviceId);
    yolov7Inputs.push_back(pastedImage.ConvertToTensor());
    ResizedImageInfo imagePreProcessInfo(resizedWidth, resizedHeight, originalWidth, originalHeight,
        RESIZER_TF_KEEP_ASPECT_RATIO, minScale);
    imagePreProcessInfos.push_back(imagePreProcessInfo);
    return APP_ERR_OK;
}

APP_ERROR DetectionInfer::DvppPreprocessor(std::string &imagePath, vector<Tensor> &yolov7Inputs,
    std::vector<ResizedImageInfo> &imagePreProcessInfos)
{
    ImageProcessor imageProcessor(m_npuDeviceId);
    if (m_isYuvInput) {
        return DvppPreprocessorYuv(imageProcessor, imagePath, yolov7Inputs, imagePreProcessInfos);
    } else {
        if (true) { // use dvpp 
            Image decodeImage;
            APP_ERROR ret = imageProcessor.Decode(imagePath, decodeImage, ImageFormat::BGR_888);
            if (ret != APP_ERR_OK) {
                LogError << "ImageProcessor decode failed.";
                return OpenCVPreProcessor(imagePath, yolov7Inputs, imagePreProcessInfos);
            }
            Image resizeImage;
            uint32_t originalWidth = decodeImage.GetOriginalSize().width;
            uint32_t originalHeight = decodeImage.GetOriginalSize().height;
            float scaleWidth = MODEL_INPUT_WIDTH * 1.0 / originalWidth;
            float scaleHeight = MODEL_INPUT_HEIGHT * 1.0 / originalHeight;
            float minScale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
            int resizedWidth = std::round(originalWidth * minScale);
            int resizedHeight = std::round(originalHeight * minScale);
            ret = imageProcessor.Resize(decodeImage, MxBase::Size(resizedWidth, resizedHeight), resizeImage,
                Interpolation::BILINEAR_SIMILAR_OPENCV);

            if (ret != APP_ERR_OK) {
                LogError << "ImageProcessor resize failed.";
                return ret;
            }
            Image pastedImage;
            std::pair<int, int> resizedInfo(resizedWidth, resizedHeight);
            ret = PaddingProcess(imageProcessor, resizedInfo, resizeImage, pastedImage);
            if (ret != APP_ERR_OK) {
                LogError << "ImageProcessor padding failed.";
                return ret;
            }
            yolov7Inputs.push_back(pastedImage.ConvertToTensor());
            ResizedImageInfo imagePreProcessInfo(resizedWidth, resizedHeight, originalWidth, originalHeight,
                RESIZER_TF_KEEP_ASPECT_RATIO, minScale);
            imagePreProcessInfos.push_back(imagePreProcessInfo);

        } else { // use opencv
            return OpenCVPreProcessor(imagePath, yolov7Inputs, imagePreProcessInfos);
        }
    }
    return APP_ERR_OK;
}

std::string DetectionInfer::E2eInfer(const std::vector<uint8_t> &imageData, double objThresh, double iouThresh)
{
    vector<Tensor> modelInputs;
    std::vector<ResizedImageInfo> imagePreProcessInfos; 

    auto image = cv::imdecode(imageData, cv::IMREAD_COLOR);
    if (image.empty()) {
        LogError << "read infer image failed.";
        //return APP_ERR_COMM_FAILURE;
        return "";
    }


    size_t originalWidth = image.cols;
    size_t originalHeight = image.rows;
    float scaleWidth = MODEL_INPUT_WIDTH * 1.0 / originalWidth;

    float scaleHeight = MODEL_INPUT_HEIGHT * 1.0 / originalHeight;
    float minScale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
    int resizedWidth = std::round(originalWidth * minScale);
    int resizedHeight = std::round(originalHeight * minScale);

    cv::Mat resizedImg;
    cv::resize(image, resizedImg, cv::Size(resizedWidth, resizedHeight));
    int leftOffset = (MODEL_INPUT_WIDTH - resizedWidth) / AVG_PARAM;
    int topOffset = (MODEL_INPUT_HEIGHT - resizedHeight) / AVG_PARAM;
    uint32_t dataSize = MODEL_INPUT_HEIGHT * MODEL_INPUT_WIDTH * RGB_EXTEND;

    MxBase::Size imageSize(MODEL_INPUT_WIDTH, MODEL_INPUT_HEIGHT);
    cv::Mat extendedImage;
    cv::copyMakeBorder(resizedImg, extendedImage, topOffset, MODEL_INPUT_HEIGHT - topOffset - resizedHeight, leftOffset,
        MODEL_INPUT_WIDTH - leftOffset - resizedWidth, cv::BORDER_CONSTANT, cv::Scalar(PAD_COLOR, PAD_COLOR, PAD_COLOR));

    uint8_t *pasteHostData = (uint8_t *)malloc(dataSize);
    if (pasteHostData == nullptr) {
        // return APP_ERR_ACL_BAD_ALLOC;
	return "";
    }

    for (size_t i = 0; i < dataSize; i++) {
        pasteHostData[i] = extendedImage.data[i];
    }

    std::shared_ptr<uint8_t> dataPaste((uint8_t *)pasteHostData, free);
    Image pastedImage(dataPaste, dataSize, -1, imageSize, ImageFormat::BGR_888);
    pastedImage.ToDevice(m_npuDeviceId);

    modelInputs.push_back(pastedImage.ConvertToTensor());
    ResizedImageInfo imagePreProcessInfo(resizedWidth, resizedHeight, originalWidth, originalHeight, 
		                         RESIZER_TF_KEEP_ASPECT_RATIO, minScale);
    imagePreProcessInfos.push_back(imagePreProcessInfo);

    auto start = std::chrono::high_resolution_clock::now();
    // infer
    vector<Tensor> detectOutputs = m_pModel->Infer(modelInputs);
    if (detectOutputs.size() == 0) {
        LogError << "detection infer failed.";
        // return APP_ERR_COMM_FAILURE;
        return "";
    }
    auto end = std::chrono::high_resolution_clock::now();

    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    // std::cout << "代码段执行时间： " << duration.count() << " 毫秒" << std::endl;

    for (size_t i = 0; i < detectOutputs.size(); i++) {
        detectOutputs[i].ToHost();
    }

    std::vector<TensorBase> outputTensors;
    for (size_t i = 0; i < detectOutputs.size(); i++) {
        MemoryData memoryData(detectOutputs[i].GetData(), detectOutputs[i].GetByteSize());
        TensorBase tensorBase(memoryData, true, detectOutputs[i].GetShape(), TENSOR_DTYPE_INT32);
        outputTensors.push_back(tensorBase);
    }

    std::vector<std::vector<ObjectInfo>> objectInfos;

    m_pModelPostProcess->SetObjectThreshold(objThresh);
    m_pModelPostProcess->SetIOUThreshold(iouThresh);
    APP_ERROR ret = m_pModelPostProcess->Process(outputTensors, objectInfos, imagePreProcessInfos);
    if (ret != APP_ERR_OK) {
        LogError << "post process failed.";
        // return APP_ERR_COMM_FAILURE;
        return "";
    }

    JsonGenerator jsonGen;
    std::string jsonStr = jsonGen.generateJson(objectInfos, (uint32_t)duration.count());
 
#if DRAW_IMAGE
    for (size_t i = 0; i < objectInfos.size(); i++) {
        for (size_t j = 0; j < objectInfos[i].size(); j++) {
		std::cout << " objectInfo-" << j;
		std::cout << "      x0 is:" << objectInfos[i][j].x0;
		std::cout << "      y0 is:" << objectInfos[i][j].y0;
		std::cout << "      x1 is:" << objectInfos[i][j].x1;
		std::cout << "      y1 is:" << objectInfos[i][j].y1;
		std::cout << "      confidence is: " << objectInfos[i][j].confidence;
		std::cout << "      classId is: " << objectInfos[i][j].classId;
		std::cout << "      className is: " << objectInfos[i][j].className;
		std::cout << std::endl;

		drawBoundingBox(image, objectInfos[i][j].className,
			       	objectInfos[i][j].x0, objectInfos[i][j].y0, 
				objectInfos[i][j].x1, objectInfos[i][j].y1);
        }
    }
    cv::imwrite("22.jpg", image);
#endif
    return jsonStr;
}
